;;;  -*- Mode: Lisp; Syntax: Common-Lisp -*- File: adventuregame.lisp
;;;  Author:     Michael Uranaka, Zachary Heilbron, Emerson Tabucol
;;;  Date:       April 14, 2011
;;;  Course:     ICS 313
;;;  Assignment: 7
;;;  Adopted structure, functions etc from Casting SPELs in Lisp A COMIC BOOK By Conrad Barski, M.D.
;;;  (http://www.lisperati.com/casting.html)

(defvar *input*)
(defvar ans)
(defvar exist)
(defvar *backpack* (list))
(defvar *checkedOnline* 'no)
(defvar *askedBJ* 'no)
(defvar *bribedJanitor* 'no)
(defvar *unlockedDoor* 'no)
(defvar *pickedUp* (list))
(defvar resp)

;define objects (global)
(defparameter *objects* '(coins elevator1 elevator3 office-keys answer-key camera
                          schedule laptop vending-machine peanut brownie bubble-gum ))

;define map of the game (global)
(defparameter *map* '(
                     (courtyard (You are in the courtyard. You are carrying a backpack.)
                                 (north door first-floor))
                     (first-floor (You are inside the POST building.)
                                  (south door courtyard)
                                  (west elevator elevator))
                      (elevator (You are standing in front of an elevator.)
                                (back hallway first-floor))
                     (third-floor (You are on the 3rd floor of POST.)
                                  (north computer-lab computer-lab)
                                  (east hallway offices))
                     (computer-lab (You are inside the computer-lab.)
                                   (south hallway third-floor))
                     (offices (You are in the hallway near BJ's and Professor
                               Reed's offices.)
                              (north door-to-bjs-office bj-office)
                              (east door-to-reeds-office reed-office)
                              (west hallway third-floor))
                     (reed-office (You are inside Professor Reed's office. Be carefull what you touch.)
                                  (back hallway offices))
                     (bj-office (You are inside BJ's office.)
                                (south hallway offices))
                     ))

;define the locations of objects (global)
(defparameter *object-locations* '((coins first-floor)
                                   (elevator1 elevator)
                                   (elevator3 third-floor)
                                   (janitor third-floor)
                                   (office-keys third-floor)
                                   (vending-machine first-floor)
                                   (bubble-gum vending-machine)
                                   (brownie vending-machine)
                                   (peanut vending-machine)
                                   (camera computer-lab)
                                   (laptop computer-lab)
                                   (handout bj-office)
                                   (answer-key reed-office)))

(defparameter *location* 'courtyard)          ; start in the courtyard

(defvar *items* '(peanut bubble-gum brownie))  ; define contents of vending machine

(defvar *waited* 'no)                      ; variable to check if the player waited 15 minutes or not

(defun wait-fifteen ()
  "Wait for 15 minutes"
  (if (have 'bubble-gum)
      (progn
       (setq *waited* 'yes)
       (format t "You chew the bubble gum to stay calm while you wait 15 minutes for her to leave.~%")
       (setq *backpack* (remove 'bubble-gum *backpack*))
       (values))
      (progn
       (format t "You get nervous and chicken out because you don't~%")
       (format t "have bubble gum to calm your nerves. YOU LOSE!!~%")
       (print-game-over)
       (values))))

(defun print-game-over ()
  "Prints game over in ascii art."
  (format t "   _________    __  _________~%")
  (format t "  / ____/   |  /  |/  / ____/~%")
  (format t " / / __/ /| | / /|_/ / __/   ~%")
  (format t "/ /_/ / ___ |/ /  / / /___   ~%")
  (format t "\\____/_/  |_/_/  /_/_____/  ~%")
  (format t "   ____ _    ____________    ~%")
  (format t "  / __ \\ |  / / ____/ __ \\ ~%")
  (format t " / / / / | / / __/ / /_/ /   ~%")
  (format t "/ /_/ /| |/ / /___/ _, _/    ~%")
  (format t "\\____/ |___/_____/_/ |_|    ~%")
  (values)
)

(defun ask-bj ()
  "Askes BJ for Professors schedule."
  (if (is-at 'handout *location* *object-locations*)
      (if (string-equal 'yes *checkedOnline*)
          (progn
            (format t "BJ gives you Professor Reed's schedule. She has class soon.~%")
            (format t "Wait for another 15 minutes or you will be caught!~%")
            (setq *askedBJ* 'yes)
            (push 'schedule *backpack*)
            (values))
          (progn
            (format t "BJ tells you to check her schedule online.~%")
            (setq *askedBJ* 'yes)
            (values)))
      (progn
        (format t "You have to be in BJ's office to ask him!~%")
        (values))))

(defun unlock-door ()
  "Unlock the door to professors office."
  (if (string-equal 'offices *location*)
      (if (member 'office-keys *backpack*)
          (progn
            (if (string-equal *waited* 'yes)
                (if (string-equal *unlockeddoor* 'no)
                    (progn
                      (format t "The door is now unlocked! you may enter at your own risk!~%")
                      (setq *unlockedDoor* 'yes)
                      (values))
                    (progn
                      (format t "The door is already unlocked!~%")
                      (values)))
                (progn
                  (format t "You didn't wait 15 minutes. Professor Reed catches you!~%")
                  (print-game-over))))
          (progn
            (format t "You need the office-key to unlock this door!~%")
            (format t "Bribe the janitor for the office-key!~%")
            (values)))
      (progn
        (format t "You have to be in the offices hallway to unlock the door!~%")
        (values))
      ))

(defun check-schedule-online ()
  "Check online for Professors's schedule."
  (if (is-at 'laptop *location* *object-locations*)
      (if (member 'laptop *backpack*)
          (progn
            (if (string-equal *askedBJ* 'yes)
                (progn
                  (format t "Printing schedule!~%Schedule added to inventory!~%")
                  (format t "Professor Reed has a class soon.~%")
                  (format t "Wait fifteen minutes before going in or you will be caught!~%")
                  (setq *checkedOnline* 'yes)
                  (push 'schedule *backpack*)
                  (values))
                (progn
                  (format t "Schedule is not available online. Ask BJ!~%")
                  (setq *checkedOnline* 'yes)
                  (values))))
          (progn
            (format t "You have to pickup the comptuer first!~%")
            (values)))
      (progn
        (format t "You arent in the computer lab!~%")
        (values))))

(defun bribe-janitor ()
  "Function to bribe janitor. Requires Brownie."
  (if (string-equal 'no *bribedJanitor*)
      (if (is-at 'janitor *location* *object-locations*)
          (if (have 'brownie)
              (progn
                (push 'office-keys *backpack*)
                (format t "The janitor gives you the office-keys in exchange for the brownie!~%")
                (format t "You can now unlock Professor Reed's door!~%")
                (setq *backpack* (remove 'brownie *backpack*))
                (setq *objects* (remove 'office-keys *objects*))
                (setq *bribedJanitor* 'yes)
                (values))
              (progn
                (format t "You don't have the brownie to bribe the janitor!~%")
                (format t "He decides to turn you in. YOU LOSE!!!~%")
                (print-game-over)
                (values)))
          (progn
            (format t "You are not by the janitor!")
            (values)))
      (progn
        (format t "You already bribed the janitor!~%")
        (values))))

(defun describe-location (location map)
  (second (assoc location map)))

(defun describe-path (path)                   ; describe where can you go
  `(there is a ,(second path) going ,(first path) from here.))

(defun describe-paths (location map)
  (apply #'append (mapcar #'describe-path (cddr (assoc location map)))))

(defun is-at (obj loc obj-loc)                ;check if object is in the location
  (eq (second (assoc obj obj-loc)) loc))

(defun describe-floor (loc objs obj-loc)      ;describe what objects you can see on the floor
 (apply #'append (mapcar (lambda (x)
                            (cond
                             ((string-equal "office-keys" x)
                              `(You see the janitor holding some keys. He looks like he could use a
                              treat.))
                             (t (cond
                                 ((or(string-equal "vending-machine" x)(string-equal "elevator1" x)(string-equal "elevator3" x))
                                  `(you see a ,x by you.))
                                 (t`(you see ,x on a table.)))) ))
                          (remove-if-not (lambda (x)
                                           (is-at x loc obj-loc))
                                         objs))))

(defun look ()                                 ; calls all the describe functions
  (append (describe-location *location* *map*)
          (describe-paths *location* *map*)
          (describe-floor *location* *objects* *object-locations*)) )

(defun walk-direction (direction)              ; movement action
  "Walk in the specified direction."
  (if (not (and (string-equal *location* 'offices) (string-equal direction 'east)))
      (let ((next (assoc direction (cddr (assoc *location* *map*)))))
        (cond (next (setf *location* (third next)) (look))
          (t '(you cant go that way.))))
      (progn
        (if (string-equal 'no *unlockedDoor*)
            (progn
              (format t "You did not unlock the door! Use the janitors office-keys to unlock the door.~%")
              (values))
            (progn
              (let ((next (assoc direction (cddr (assoc *location* *map*)))))
                (cond (next (setf *location* (third next)) (look))
                  (t '(you cant go that way.)))))))))

(defmacro defspel (&rest rest) `(defmacro ,@rest)) ;enable SPEL

(defspel walk (direction)                       ;use walk to point to walk-direction
  `(walk-direction ',direction))

(defun pickup-object (object)                   ;action to pick up an object
  (if (is-at object *location* *object-locations*); make sure object is in the same location
      (if (or(string-equal "vending-machine" object) (string-equal "elevator1" object) (string-equal "elevator3" object)) ; cannot pickup these
          (progn
            (format t "Are you crazy? Thats too heavy. You can't carry that!")
            (values))
          (progn
            (if (member object *pickedUp*)
                (progn
                  (format t "You already picked that up! Item isn't there anymore!")
                  (values))
                (progn
                  (push object *backpack*)
                  (push object *pickedUp*)
                  (setq *objects* (remove object *objects*))
                  (format t "You are now carrying the ~a ~%" object)
                  (values)))))
      (progn
        (format t "you cannot pick that up.")
        (values))))

(defspel pickup (object)                        ;use pickup to point to pickup-object
  `(pickup-object ',object))

(defun use-object (object) ;
  (cond
    ((is-at object *location* *object-locations*); make sure player is in the same location as the object
      (cond
       ((string-equal "vending-machine" object) ; if using the vending-machine
        (cond
         ((have 'coins)                         ; make sure you have the coins
          (vend))                               ; vend items
         (t `(You cannot use the ,object because you are broke!)))) ; if no coins, then no vend
       ((string-equal "elevator1" object)       ; if using elevator1
         (setq *location* 'third-floor) (look)) ; it'll take you to the 3rd floor
       ((string-equal "elevator3" object)       ; if using elevator3
         (setq *location* 'elevator) (look)) ; it'll take you to the 1st floor
        ))
    (t '(you cannot use that.))))

(defspel use (object)                           ; use spel (for vending-machine and elevators)
  `(use-object ',object))

(defun backpack ()
  "Prints out the current inventory."
  (princ *backpack*)
  (values))

(defun have (object)                            ; check if player have the object
  (member object *backpack*))

(defun get-user-input ()
  "Retrieve user's input."
  (setq ans (read))                             ; read user's input
  ans
)

(defun vend ()
  (format t "You can dispense two items.~%")
  (setq *location* 'vending-machine)            ; set the location temporarylily to vending-machine
  (let ((num 0))

    (loop while (< num 2) do
          (format t "Vending-machine contains:")
          (display-items)                       ; display items in the vending machine
          (format t "~%Pick one: " )            ; ask for choice
          (setq *input* (get-user-input))       ; get player's choice
          (setq exist (position *input* *items*)); check if item exist in the vending-machine

          (cond
           ((and (not (null *input*)) (not(null exist)) ); make sure input is valid
            (take-item *input*)(setq num (+ num 1)) ; take the item and increment number of items taken
            (format t "You now have the ~a~%~%" *input*)
            (if (eq num 2) (setq *backpack* (remove 'coins *backpack*)))) ; remove coins
           (t (format t "Sorry, ~a is not available!~%~%" *input*)))                   ;
          ))
  (setq *location* 'first-floor)(values)        ; set the location back to 1st floor and return nothing
)

(defun take-item (item)
  (if (string-equal item "brownie")             ; if player like brownie
      (pickup brownie))                         ; take it
  (if (string-equal item "bubble-gum")
      (pickup bubble-gum))
  (if (string-equal item "peanut")
      (pickup peanut))
  (setq *items* (remove item *items*))          ; remove item from the vending machine
)

(defun display-items ()                         ; display items in the vending machine
 (loop for x in *items*
      do (print x) )(values))

(defun get-interpreter()
  (format t "Are you running Allegro Common Lisp 6.0 (uhunix)?:")
  (setq resp (read))
  (if (or (string-equal resp "yes") (string-equal resp "y"))
      (eval (read-from-string "(setf tpl:*print-length* nil)")))
  (format t "~%~%")
)

(defun play-game ()
  "Start of the game."
  (get-interpreter)
  (format t "Your final exam is today.~%")
  (format t "You didn't have time to study because you were too busy partying all week.~%")
  (format t "The only way to pass the exam is to steal it!~%")
  (setq *items* '(peanut bubble-gum brownie))    ; put items in the vending machine
  ;(setf tpl:*print-length* nil)
  (look)
)

(defun take-picture ()
  "Take the picture of the answer key."
  (if (is-at 'answer-key *location* *object-locations*)
      (if (member 'camera *backpack*)
          (progn
            (format t "You have taken a picture of the answer-key!~%The final is going to be a piece of cake!!!!~%")
            (print-you-win))
          (progn
            (format t "You don't have the camera!~%Go to the computer lab to borrow a camera!~%")
            (values)))
      (progn
        (format t "You are not in professor reeds lab!~%")
        (values)))
  )

(defun print-you-win ()
  "Prints you win in ascii art."
  (format t "__  ______  __  __   _       _______   __   ~%")
  (format t "\\ \\/ / __ \\/ / / /  | |     / /  _/ | / /~%")
  (format t " \\  / / / / / / /   | | /| / // //  |/ /   ~%")
  (format t " / / /_/ / /_/ /    | |/ |/ // // /|  /     ~%")
  (format t "/_/\\____/\\____/     |__/|__/___/_/ |_/    ~%")
  (values))
